//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Text;
using System.Windows.Documents;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Extensibility;

namespace OpenLS.Spreadsheet
{
    /// <summary>
    /// Represents a range made of simple ranges
    /// </summary>
    public partial class CompositeRange : Range
    {
        public override bool Equals(Range r)
        {

            if (r == this)
                return true;
            if (this.Ranges.Count == 1)
                return Ranges[0].Equals(r);
            CompositeRange cr = r as CompositeRange;
            if (cr == null)
                return false;
           List<SimpleRange> myRanges = new List<SimpleRange>(this._ranges);
            List<SimpleRange> otherRanges = new List<SimpleRange>(cr.Ranges);
            if (myRanges.Count != otherRanges.Count)
                return false;
            foreach (var range in myRanges)
            {
                var match = Enumerable.FirstOrDefault(otherRanges, rr => rr.Equals(range));
                if (match == null)
                    return false;
                otherRanges.Remove(match);
            }
            return true;
        }
        internal override IEnumerable<CellFormattingRecord> GetFormattingRecords()
        {
            return Enumerable.SelectMany(this.Ranges, r => r.GetFormattingRecords());
        }
        internal override IEnumerable<Range> GetColumnRanges(RangeType type)
        {
            return Enumerable.SelectMany(Ranges, r => r.GetColumnRanges(type));
        }
        internal override IEnumerable<Range> GetRowRanges(RangeType type)
        {
            return Enumerable.SelectMany(Ranges, r => r.GetRowRanges(type));
        }
        
        private readonly List<SimpleRange> _ranges;

        public CompositeRange(IEnumerable<SimpleRange> ranges)
        {
            Contract.Requires(ranges != null);
#if DEBUG
            Contract.EndContractBlock();
#endif
            Contract.Assert(Enumerable.Count(ranges) > 1);
            Contract.Assert(Enumerable.All(ranges, r => r != null));
            if (ranges == null)
                throw new ArgumentException("ranges");
            if (!Enumerable.All(ranges, r => r != null))
                throw new ArgumentException("ranges");
          
            _ranges = Enumerable.ToList(ranges);
#if DEBUG
            for (int i = 0; i < _ranges.Count; i++ )
            {
                for (int j = 0; j < _ranges.Count; j++)
                {
                    if (j == i)
                        continue;
                 //\\   Contract.Assert(!_ranges[i].Contains(_ranges[j]));  //\\not sure about his one
                }
            }
#endif
        }

        public List<SimpleRange> Ranges
        {
            get { return _ranges; }
        }

        internal override IEnumerable<SimpleRange> GetSimpleRanges()
        {
            return Ranges;
        }

        internal override void SetBorderNoUndo(Border border, BorderType borderType)
        {
            foreach (var range in Ranges)
            {
                range.SetBorderNoUndo(border, borderType);
            }
        }

        public override Cell TopLeftCell
        {
            get { return Ranges[0].TopLeftCell; //\\??
            }
        }

        internal override Cell FirstCell
        {
            get { return Ranges[0].FirstCell; }
        }

        public override int RowSpan
        {
            get { return Ranges[0].RowSpan; }
        }

        public override int ColumnSpan
        {
            get { return Ranges[0].ColumnSpan; }
        }

        public override bool IsFullRow
        {
            get { return false; }
        }

        public override bool IsFullColumn
        {
            get { return false; }
        }

        public override Worksheet Worksheet
        {
            get { return Ranges[0].Worksheet; }
        }

        public override string Label
        {
            get
            {
                return "(" + StringEnumerable.Concat(Ranges, rr => rr.Label, ",") + ")";

                var b = new StringBuilder();
                b.Append('(');
                Range last = Ranges[Ranges.Count - 1];
                foreach (Range r in Ranges)
                {
                    b.Append(r.Label);
                    if (r != last)
                        b.Append(',');
                }
                b.Append(')');
                return b.ToString();
            }
        }

        internal override string LabelForSelection
        {
            get
            {
                return StringEnumerable.Concat(Ranges, rr => rr.Label, ' ');
                var b = new StringBuilder();
                Range last = Ranges[Ranges.Count - 1];
                foreach (Range r in Ranges)
                {
                    b.Append(r.Label);
                    if (r != last)
                        b.Append(' ');
                }
                return b.ToString();
            }
        }

        internal override bool IsSingleCell
        {
            get
            {
                return this.Ranges.Count == 1 && Ranges[0].IsSingleCell;
            }
        }

        internal override bool Contains(Range range)
        {
            foreach (Range r in _ranges)
            {
                if (r.Contains(range))
                    return true;
            }
            return ContainsCompositeRange(range);
        }

       /* internal override Range Projected(Worksheet worksheet, CellRelativeAddress diff)
        {
            var rr = new List<SimpleRange>();
            foreach (var r in _ranges)
                rr.Add(r.Projected(worksheet, diff)); //\\sure?
            return new CompositeRange(rr);
        }*/

        internal override int GetCellIndex(Cell c)
        {
            int result = 0;
            foreach (Range r in Ranges)
            {
                if (r.Contains(c))
                    return result + r.GetCellIndex(c);
                else
                    result += r.Surface;
            }
            throw new NotSupportedException();
        }

        internal override void SetCellValueNoUndo(CellProperty p, object value)
        {
            foreach (var range in this.Ranges)
            {
                range.SetCellValueNoUndo(p, value);
            }
        }

        internal override CellFormattingRecord GetFormattingRecord()
        {
            return TopLeftCell.FormattingRecord;
        }

        internal override object GetCellValue(CellProperty p, out bool uniform)
        {
            object result = null;
            uniform = true;
            foreach (Range range in Ranges)
            {
                result = range.GetCellValue(p, out uniform);
                if (!uniform)
                    return result;
            }
            return result;
        }

        internal override Border GetBorder(BorderType borderType, out bool uniform)
        {
            uniform = false;
            return null;
        }

        internal override IEnumerable<Cell> GetNonEmptyCells()
        {
            foreach (Range r in Ranges)
            {
                foreach (Cell cell in r.GetNonEmptyCells())
                    yield return cell;
            }
        }

        internal override bool Contains(CellAddress address)
        {
            foreach (Range r in Ranges)
                if (r.Contains(address))
                    return true;
            return false;
        }

        internal override RangeReference ToReference()
        {
            var references = new List<RangeReference>();
            foreach (Range r in Ranges)
                references.Add(r.ToReference());
            return new RangeListReference(references);
        }

        internal override FlowDocument ToFlowDocument(string columnSeparator)
        {
            var worksheets = new List<Worksheet>(Enumerable.Distinct(Enumerable.Select(Ranges, r => r.Worksheet)));
            if (worksheets.Count != 1 )
                throw new NotImplementedException();
            var worksheet = Ranges[0].Worksheet;
            int row = Enumerable.Min(Ranges, r => r.Row);
            int column = Enumerable.Min(Ranges, r => r.Column);
            int rowMax = Enumerable.Min(Ranges, r => r.Row + r.RowSpan - 1);
            int columnMax = Enumerable.Min(Ranges, r => r.Column + r.ColumnSpan - 1);
            var rangeAddress = new RangeAddress(new CellAddress(row, column),
                                                new CellAddress(rowMax, columnMax));
            Range mcr;
            if (rangeAddress.IsFullColumn)
                mcr = new FullColumnRange(worksheet.Columns[column], worksheet.Columns[columnMax]);
            else if (rangeAddress.IsFullRow)
                mcr = new FullRowRange(worksheet.Rows[row], worksheet.Rows[rowMax]);
            else
                mcr = new MultiCellsRange(Ranges[0].Worksheet,
                                          rangeAddress);
            return mcr.ToFlowDocument(columnSeparator);
        }

        public override IEnumerable<Cell> GetAllCells()
        {
            return Enumerable.SelectMany(Ranges, r => r.GetAllCells());
        }

        internal override Range Offseted(CellRelativeAddress address)
        {
            throw new NotImplementedException();
        }

        public override IEnumerable<Column> GetColumns()
        {
            return Enumerable.Distinct(Enumerable.SelectMany(this.Ranges, r => r.GetColumns()));
        }

        public override IEnumerable<Row> GetRows()
        {
            return Enumerable.Distinct(Enumerable.SelectMany(this.Ranges, r => r.GetRows()));
       }

        internal static Range CreateWithLast(CompositeRange range, SimpleRange lastRange)
        {
            return
                new CompositeRange(Enumerable.Union(Enumerable.Take(range.Ranges, range.Ranges.Count - 1),
                                                    new SimpleRange[] {lastRange}));
        }

        internal override void SetStyleNoUndo(CellStyle s)
        {
            foreach (var range in Ranges)
                range.SetStyleNoUndo(s);
        }

        internal override Cell GetNextCell(Cell cell, CellNavigationDirection direction)
        {
            
            var range = Enumerable.First(Ranges, r => r.Contains(cell));

            if (cell == range.FirstCell && direction == CellNavigationDirection.Backward)
            {
                int i = Ranges.IndexOf(range);
                i--;
                if (i < 0)
                    i = Ranges.Count - 1;
                range = Ranges[i];
                return range.GetNextCell(range.FirstCell, CellNavigationDirection.Backward);
            }
            var result = range.GetNextCell(cell, direction);
            if (result == range.FirstCell && direction == CellNavigationDirection.Forward)
            {
                int i = Ranges.IndexOf(range);
                i++;
                if (i > Ranges.Count - 1)
                    i = 0;
                range = Ranges[i];
                return range.FirstCell;
            }
            return result;
           
        }

    }
}